package com.atguigu.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.util.Date;

@Configuration
public class MQListener {

    //通过@RabbitListener注解 标注的方法就是一个mq的消费者
    @RabbitListener(
//            queues = 如果已经有交换机 和绑定到交换机上的队列，直接通过queues指定队列名称
            bindings = @QueueBinding( //相当于初始化QueueBinding 绑定队列和交换机
                    //@Queue初始化队列    name队列名称， durable代表队列持久化
                    value = @Queue(name = "spring_test1_queue" , durable = "true"),
                    //@Exchange初始化交换机 ， name：交换机名称 必须和生产者发送消息的交换机名称一样
                    //ignoreDeclarationExceptions 交换机可以重复声明
                    // type = ExchangeTypes.TOPIC  指定交换机的类型：只有Topic才可以使用通配符
                    exchange = @Exchange(name = "spring.test1.exchange", type = ExchangeTypes.TOPIC
                            ,durable = "true" , ignoreDeclarationExceptions = "true"),
                    //当前队列绑定到交换机上时，监听消息的路由key(以后交换机中的消息路由key和本处配置的一样才会将消息路由给当前队列处理)
                    // 路由key字符串如果完全相同 可以匹配
                    // 路由key可以通过通配符进行匹配： #(通配所有)   *(一层任意多个字符)
                    key = "a.*.*"
            )
    )
    //参数1 代表接收到的消息内容名，参数2：代表 消息对象(包含消息的唯一标记)  参数3：代表通道 用来确认消息时使用
    public void test1Consumer(String msg , Message message , Channel channel){
        try {
            System.out.println("消费者接受到消息："+msg);
            int i = 1/0;//手动制造异常   死信队列+延迟队列  解决手动ack时 异常消息问题
            //手动确认消息被消费：如果配置了手动ack，只有此方法调用成功  mq才会将消息队列中的消息删除
            //参数1： 消息的唯一标记      参数2：是否批量处理其他的消息ack
            channel.basicAck(message.getMessageProperties().getDeliveryTag() ,false);
//        channel.basicReject();  不确认消息 ，并拒绝消息 丢弃消息
//        channel.basicNack();  不确认消息，让消息重新回到队列中
        } catch (Exception e) {
            e.printStackTrace();
            try {
                if(message.getMessageProperties().isRedelivered()){
                    //如果消息第二次消费之后仍然失败，丢弃消息
                    //参数2： false表示消息不重新归队
                    channel.basicReject(message.getMessageProperties().getDeliveryTag() ,false);
                }else{
                    //如果消息第一次消息失败，让消息重新归队 再次尝试消费
                    //参数1：消息唯一标记 ， 参数2：批量操作  参数3：消息是否重新回到队列  true重新归队
                    channel.basicNack(message.getMessageProperties().getDeliveryTag() , false , true);
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }

        }

    }

    //消息丢弃时 使用死信队列接收存储
    @RabbitListener(
            queues = "test2_service_queue" //使用已经在配置类中初始化的队列 来消费
    )
    public void test2Consumer(String msg , Message message , Channel channel){
        try {
            System.out.println("test2获取到消息: "+msg);
            int i = 1/0;
            channel.basicAck(message.getMessageProperties().getDeliveryTag() , false);
        } catch (Exception e) {
            e.printStackTrace();
            try {
                if(message.getMessageProperties().isRedelivered()){
                    channel.basicReject(message.getMessageProperties().getDeliveryTag() , false);
                }else{
                    channel.basicNack(message.getMessageProperties().getDeliveryTag() , false,true);
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }


    //延迟队列消费者
    @RabbitListener(queues = "test2_delay_queue")
    public void test2DelayConsumer(String msg , Message message , Channel channel){
        try {
            System.out.println("延迟队列消费者消费消息："+msg+" , 当前时间："+ new Date());
            channel.basicAck(message.getMessageProperties().getDeliveryTag()  , false);
        } catch (IOException e) {
            e.printStackTrace();
            //将不能处理的消息持久化到日志、redis、数据库表中
        }
    }
}
